#region Apache License
//
// Licensed to the Apache Software Foundation (ASF) under one or more 
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership. 
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with 
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion



using System;
using System.Drawing;
using System.Drawing.Drawing2D;

using IPSim.GUI.Devices;
using IPSim.Util;

namespace IPSim.GUI
{
    public class NetworkDesign
    {
        #region Members

        private     Point               location            = new Point(100, 100);
        private     Size                size                = new Size(0, 0);
        private     Size                windowSize          = new Size(0, 0);
        private     Size                gridSize            = new Size(20, 20);
        private     bool                showGrid            = true;

        internal    DeviceCollection    devices             = new DeviceCollection();
        private     DeviceCollection    selectedDevices     = new DeviceCollection();

        private     DesignerAction      action              = DesignerAction.Select;
        private     DeviceType          deviceType          = DeviceType.Rectangle;
        private     LineType            lineType            = LineType.Straight;

        private     SmoothingMode       smoothingMode       = SmoothingMode.HighQuality;
        private     PixelOffsetMode     pixelOffsetMode     = PixelOffsetMode.Default;
        private     CompositingQuality  compositingQuality  = CompositingQuality.AssumeLinear;

        #endregion

        #region Methods

        public NetworkDesign()
        {
        }

        #region Devices Managment methods

        public void AddDevice(BaseDevice device)
        {
            Logger.log("NetworkDesign - Adding device:" + device.Type);
            devices.Add(device);
            device.AppearanceChanged += new EventHandler(device_AppearanceChanged);
            OnAppearancePropertyChanged(new EventArgs());
        }

        public void DeleteDevice(BaseDevice device)
        {
            if (device != null)
            {
                devices.Remove(device);                
                OnAppearancePropertyChanged(new EventArgs());
            }
            device = null;
        }

        public void DeleteDevice(Point p)
        {
            BaseDevice device = FindDevice(p);
            DeleteDevice(device);
        }

        public void ClearSelection()
        {
            selectedDevices.Clear();
        }

        public void SelectDevice(BaseDevice device)
        {
            if(device is ConnectorDevice)
                selectedDevices.Add(((ConnectorDevice)device).ParentDevice);
            else
                selectedDevices.Add(device);
            OnPropertyChanged(new EventArgs());
        }

        public BaseDevice FindDevice(Point point)
        {
            BaseDevice device;
            if ((devices != null) && (devices.Count > 0))
            {
                for (int i = devices.Count - 1; i >= 0; i--)
                {
                    device = devices[i];

                    foreach (BaseDevice conn in device.Connectors)
                    {
                        IController ctrl = ((IControllable)conn).GetController();
                        if (ctrl.IsInside(point))
                            return conn;
                    }
                }
                for (int i = devices.Count - 1; i >= 0; i--)
                {
                    device = devices[i];
                    if (device is IControllable)
                    {
                        IController controller = ((IControllable)device).GetController();
                        if (controller.IsInside(point))
                            return device;
                    }
                }
            }
            return null;
        }

        public BaseLinkDevice CreateLink(ConnectorDevice connStart, ConnectorDevice connEnd)
        {
            if (CanCreateLink(connStart, connEnd))
            {
                BaseLinkDevice link = new StraightLinkDevice(connStart, connEnd);
                devices.Add(link);
                link.AppearanceChanged += new EventHandler(device_AppearanceChanged);
                OnAppearancePropertyChanged(new EventArgs());
                return link;
            }
            else
            {
                return null;
            }
        }

        internal bool CanCreateLink(ConnectorDevice connStart, ConnectorDevice connEnd)
        {
            return ((connStart != connEnd) && (connStart.ParentDevice != connEnd.ParentDevice));
        }

        #endregion

        #region Drawing methods

        internal void DrawElements(Graphics g, Rectangle clippingRegion)
        {
            foreach (BaseDevice device in devices)
            {
                if(device is BaseLinkDevice)
                    device.Draw(g);
            }
            foreach (BaseDevice device in devices)
            {
                if (!(device is BaseLinkDevice))
                    device.Draw(g);                
            }
        }

        internal void DrawSelections(Graphics g, Rectangle clippingRegion)
        {
            for (int i = selectedDevices.Count - 1; i >= 0; i--)
            {
                if (selectedDevices[i] is IControllable)
                {
                    IController ctrl = ((IControllable)selectedDevices[i]).GetController();
                    ctrl.DrawSelection(g);
                }
            }
        }

        internal void DrawGrid(Graphics g, Rectangle clippingRegion)
        {
            if (showGrid)
            {
                Pen p = new Pen(new HatchBrush(HatchStyle.LargeGrid | HatchStyle.Percent90, Color.LightGray, Color.Transparent), 1);
                int maxX = location.X + this.Size.Width;
                int maxY = location.Y + this.Size.Height;

                if (windowSize.Width> maxX)
                    maxX = windowSize.Width;

                if (windowSize.Height > maxY)
                    maxY = windowSize.Height;
                                
                for (int i = 0; i < maxX; i += gridSize.Width)
                {
                    g.DrawLine(p, i, 0, i, maxY);
                }

                for (int i = 0; i < maxY; i += gridSize.Height)
                {
                    g.DrawLine(p, 0, i, maxX, i);
                }
            }
        }

        internal void CalcWindow()
        {
            devices.CalcWindow();
        }

        #endregion

        #endregion

        #region Properties


        public bool ShowGrid
        {
            get
            {
                return showGrid;
            }
            set 
            {
                showGrid = value;
                OnAppearancePropertyChanged(new EventArgs());
            }
        }
        public DeviceCollection Devices
        {
            get
            {
                return devices;
            }
        }

        public DeviceCollection SelectedDevices
        {
            get
            {
                return selectedDevices;
            }
        }

        public Point Location
        {
            get
            {
                return devices.Location;
            }
        }

        public Size WindowSize
        {
            get
            {
                return windowSize;
            }
            set
            {
                windowSize = value;
                OnAppearancePropertyChanged(new EventArgs());
            }
        }

        public Size Size
        {
            get
            {
                return devices.Size;
            }
        }

        public Size GridSize
        {
            get
            {
                return gridSize;
            }
            set
            {
                gridSize = value;
                OnAppearancePropertyChanged(new EventArgs());
            }
        }

        public DesignerAction Action
        {
            get
            {
                return action;
            }
            set
            {
                action = value;
                OnPropertyChanged(new EventArgs());
            }
        }

        public DeviceType DeviceType
        {
            get
            {
                return deviceType;
            }
            set
            {
                deviceType = value;
                OnPropertyChanged(new EventArgs());
            }
        }

        public LineType LineType
        {
            get
            {
                return lineType;
            }
            set
            {
                lineType = value;
                OnPropertyChanged(new EventArgs());
            }
        }

        public SmoothingMode SmoothingMode
        {
            get
            {
                return smoothingMode;
            }
            set
            {
                smoothingMode = value;
                OnAppearancePropertyChanged(new EventArgs());
            }
        }

        public PixelOffsetMode PixelOffsetMode
        {
            get
            {
                return pixelOffsetMode;
            }
            set
            {
                pixelOffsetMode = value;
                OnAppearancePropertyChanged(new EventArgs());
            }
        }

        public CompositingQuality CompositingQuality
        {
            get
            {
                return compositingQuality;
            }
            set
            {
                compositingQuality = value;
                OnAppearancePropertyChanged(new EventArgs());
            }
        }

        #endregion

        #region Events

        public event EventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(EventArgs e)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, e);
        }

        public event EventHandler AppearancePropertyChanged;

        protected virtual void OnAppearancePropertyChanged(EventArgs e)
        {
            OnPropertyChanged(e);
            if (AppearancePropertyChanged != null)
                AppearancePropertyChanged(this, e);

        }

        public event EventHandler DevicePropertyChanged;

        protected virtual void OnDevicePropertyChanged(object sender, EventArgs e)
        {
            if (DevicePropertyChanged != null)
                DevicePropertyChanged(sender, e);
        }

        private void device_AppearanceChanged(object sender, EventArgs e)
        {
            OnDevicePropertyChanged(sender, e);
        }

        private void RecreateEventsHandlers()
        {
            foreach (BaseDevice device in devices)
                device.AppearanceChanged += new EventHandler(device_AppearanceChanged);
        }

        #endregion
    }
}
